package org.web30.nntp.webservice;

import com.sun.syndication.feed.rss.Channel;
import com.sun.syndication.feed.rss.Item;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;
import java.io.BufferedWriter;
import java.net.SocketException;
import java.net.URISyntaxException;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.message.Message;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.*;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.james.mime4j.field.AbstractField;
import org.apache.james.mime4j.field.ParsedField;
import org.web30.bookmark.Bookmark;
import org.web30.bookmark.Bookmarks;
import org.web30.feed.FeedBuilder;
import org.web30.feed.MimeMessageBuilder;
import org.web30.nntp.service.NNTPService;
import org.web30.nntp.service.NNTPService.NewsRecords;

/**
 * REST Web Service
 *
 * @author Xia Zhenyu
 */
@Path("/news/{group}")
public class NNTPResource {

    @Context
    UriInfo uriInfo;
    @Context
    HttpHeaders requestHeader;
    
    private static Logger logger = Logger.getLogger(NNTPResource.class.getName());
    private static FileHandler fh = null;
    private String nntpHostName = "211.103.244.159";

    /** Creates a new instance of NNTPResource */
    public NNTPResource() throws IOException {
    }

    /**
     * Retrieves representation of an instance of NNTPResource
     * @return an instance of java.lang.String
     */
    @Path("/daily")
    @GET
    @Produces("application/rss+xml")
    public String getDailyNews(@PathParam("group") String groupName)
            throws IOException, FeedException, URISyntaxException {
        NNTPService srv = new NNTPService(nntpHostName,
                7500,
                "xutj@nec-as.nec.com.cn",
                "eds321");
        srv.init();
        logger.info("group name:" + groupName);
        NewsRecords records = null;
        records = srv.getDailyNews(groupName);
        if (!records.hasNext()) {
            srv.close();
            return "<p>IT Mingong, waiting for you GUAN HUI</p>";
        } else {
            ArrayList<Message> articles = new ArrayList<Message>(records.getRecordCount());
            while (records.hasNext()) {
                articles.add(records.nextMime());
            }
            srv.close();
            FeedBuilder build = new FeedBuilder(uriInfo.getAbsolutePath(),
                    new URI(uriInfo.getBaseUri() + "news/" + groupName + "/msg/"),
                    new URI(uriInfo.getBaseUri() + "news/" + groupName + "/comments/"),
                    false,
                    FeedBuilder.RSS);
            return FeedBuilder.feed2String(build.mime2Rss(articles));
        }
    }
    /**
     * Return message as XHTML
     * @param groupName  Group Name
     * @param articleId  message ID
     * @return
     * @throws IOException
     * @throws FeedException
     * @throws URISyntaxException
     */
    @Path("/msg/{articleId}")
    @GET
    @Produces("text/html")
    public String getNewsbById(@PathParam("group") String groupName,
            @PathParam("articleId") String articleId) throws IOException,
            FeedException,
            URISyntaxException {
        NNTPService srv = new NNTPService(nntpHostName,
                7500,
                "xutj@nec-as.nec.com.cn",
                "eds321");
        srv.init();
        logger.info("group name:" + groupName);
        NewsRecords records = srv.getNewsById(groupName, "<" + articleId + ">");
        if (!records.hasNext()) {
            srv.close();
            return null;
        } else {
            ArrayList<Message> articles = new ArrayList<Message>(records.getRecordCount());
            while (records.hasNext()) {
                articles.add(records.nextMime());
            }
            srv.close();
            FeedBuilder build = new FeedBuilder(uriInfo.getAbsolutePath(),
                    new URI(uriInfo.getBaseUri() + "news/" + groupName + "/msg/"),
                    new URI(uriInfo.getBaseUri() + "news/" + groupName + "/comments/"),
                    true, 
                    FeedBuilder.RSS);
            Channel channel = build.mime2Rss(articles);
            Item item = (Item)channel.getItems().get(0);
            if(item == null)
                return FeedBuilder.feed2String(channel);
            return item.getContent().getValue();
        }
    }

    @POST
    @Consumes("application/atom+xml")
    public Response postEntry(@PathParam("group") String groupName,
            java.io.InputStream in) throws IOException {
        SyndFeedInput input = new SyndFeedInput();
        SyndFeed feed = null;
        try {
            feed = input.build(new XmlReader(in));
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return Response.status(415).build();
        } catch (FeedException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return Response.status(415).build();
        }
        UriBuilder entryUriBuilder = uriInfo.getAbsolutePathBuilder().path("msg/");
        URI entryUri = entryUriBuilder.build();
        List<SyndEntry> entries = feed.getEntries();
        ArrayList<SyndEntry> returnEntries = new ArrayList<SyndEntry>(entries.size());
        for (SyndEntry e : entries) {
            e.setLink(entryUri.toASCIIString());
            SyndEntry temp = this.postNNTP(e, groupName);
            if (temp != null) {
                returnEntries.add(temp);
            }
        }
        return Response.created(entryUri).build();
    }
    /**
     * Create new entry with JSON API
     * @param groupName
     * @param bookmarks 
     * @return
     */
    @POST
    @Consumes("application/json")
    public synchronized Response postJSONEntry(@PathParam("group") String groupName,
            Bookmarks bookmarks) {
        MediaType type = requestHeader.getMediaType();
        Map map = type.getParameters();
        String charset = (String) map.get("charset");
        if(charset!= null){
            charset = charset.toUpperCase();
        }
        if (bookmarks == null) {
            return Response.status(415).build();
        }
        try {
            for (Bookmark bookmark : bookmarks.getBookmark()) {
                this.postNNTP(bookmark, groupName, null);
            }
        } catch (Exception e) {
            return Response.status(415).build();
        }
        UriBuilder entryUriBuilder = uriInfo.getAbsolutePathBuilder().path("msg/");
        URI entryUri = entryUriBuilder.build();
        return Response.created(entryUri).entity(bookmarks).build();
    }
    /**
     * Create a comment for specific message
     * @param groupName  group name
     * @param articleId  message id that comment on
     * @param bookmarks
     * @return
     */
    @Path("/comments/{articleId}")
    @POST
    @Consumes({"application/json"})
    public synchronized Response postComment(
            @PathParam("group") String groupName,
            @PathParam("articleId") String articleId,
            Bookmarks bookmarks) {
        if (bookmarks == null) {
            return Response.status(415).build();
        }
        try {
            for (Bookmark bookmark : bookmarks.getBookmark()) {
                this.postNNTP(bookmark, groupName, articleId);
            }
        } catch (Exception e) {
            return Response.status(415).build();
        }
        UriBuilder entryUriBuilder = uriInfo.getBaseUriBuilder().path("news/"+groupName+"/msg/");
        URI entryUri = entryUriBuilder.build();
        return Response.created(entryUri).entity(bookmarks).build();
    }
    /**
     * Return comments related to specific message
     * @param groupName group name
     * @param articleId  message id
     * @return
     * @throws SocketException
     * @throws IOException
     * @throws URISyntaxException
     * @throws FeedException
     */
    @Path("/comments/{articleId}")
    @GET
    @Produces("application/rss+xml")
    public synchronized String getCommentsById(
            @PathParam("group") String groupName,
            @PathParam("articleId") String articleId) throws SocketException, IOException, URISyntaxException, FeedException {
        NNTPService srv = new NNTPService(nntpHostName,
                7500,
                "xutj@nec-as.nec.com.cn",
                "eds321");
        srv.init();
        logger.info("group name:" + groupName);
        NewsRecords records = srv.getCommentsById(groupName, "<" + articleId + ">");
        if (!records.hasNext()) {
            srv.close();
            return null;
        } else {
            ArrayList<Message> articles = new ArrayList<Message>(records.getRecordCount());
            while (records.hasNext()) {
                articles.add(records.nextMime());
            }
            srv.close();
            FeedBuilder build = new FeedBuilder(uriInfo.getAbsolutePath(),
                    new URI(uriInfo.getBaseUri() + "news/" + groupName + "/msg/"),
                    new URI(uriInfo.getBaseUri() + "news/" + groupName + "/comments/"),
                    true,
                    FeedBuilder.RSS);
            return FeedBuilder.feed2String(build.mime2Rss(articles));
        }
    }

    private Bookmark postNNTP(Bookmark b, String groupName, String messageId) {
        try {
            NNTPService srv = new NNTPService(nntpHostName, 7500, "xutj@nec-as.nec.com.cn", "eds321");
            srv.init();
            NNTPService.NewsWriter nw = srv.postNews(groupName);
            if (nw == null) {
                logger.severe("News Writer is null failed to post");
                srv.close();
                return null;
            }
            Message msg = MimeMessageBuilder.Bookmark2Mime(b);
            ParsedField field = AbstractField.parse("Newsgroups: " + groupName);
            msg.getHeader().addField(field);
            if (messageId != null) {
                ParsedField reply = AbstractField.parse("In-Reply-To: <" + messageId + ">");
                msg.getHeader().addField(reply);
            }
            if (messageId != null) {
                String references = srv.getReferenceById(groupName, "<" + messageId + ">");
                if (references != null) {
                    ParsedField refers = AbstractField.parse("References: "
                            + references + " <" + messageId + ">");
                    msg.getHeader().addField(refers);
                } else {
                    ParsedField refers = AbstractField.parse("References: "+
                            "<" + messageId + ">");
                    msg.getHeader().addField(refers);
                }
            }
            BufferedWriter output = nw.next();
            //Due to NNTPClient using ISO-8859-1 as default charset, here force to
            //convert string to ISO-8859-1 and write into NNTP stream
            output.write(MimeMessageBuilder.message2String(msg,
                    org.web30.nntp.Util.DEFAULT_ENCODING));
            output.close();
            boolean ret = nw.completePendingCommand();
            if(ret){
                String id = nw.getLastId();
                if (id != null) {
                    b.setId(id);
                }
                srv.close();
                return b;
            } else {
                return null;
            }
        } catch (MimeException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (SocketException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (IOException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    private SyndEntry postNNTP(SyndEntry e, String groupName) {
        try {
            NNTPService srv = new NNTPService(nntpHostName, 7500, "xutj@nec-as.nec.com.cn", "eds321");
            srv.init();
            NNTPService.NewsWriter nw = srv.postNews(groupName);
            if (nw == null) {
                logger.severe("News Writer is null failed to post");
                srv.close();
                return null;
            }
            MimeMessageBuilder builder = new MimeMessageBuilder();
            Message msg = builder.Entry2Mime(e);
            ParsedField field = AbstractField.parse("Newsgroups: " + groupName);
            msg.getHeader().addField(field);
            BufferedWriter output = nw.next();
            String link = e.getLink();
            output.write(MimeMessageBuilder.message2String(msg,
                    org.web30.nntp.Util.DEFAULT_ENCODING));
            output.close();
            srv.close();
            e.setLink(link + nw.getLastId());
            return e;
        } catch (MimeException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (SocketException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (IOException ex) {
            Logger.getLogger(NNTPResource.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }
}
